package vip.web3.api.service.impl.base;

import com.portto.solana.web3.util.TweetNaclFast;
import com.portto.solana.web3.wallet.DerivableType;
import com.portto.solana.web3.wallet.SolanaBip44;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.bitcoinj.core.Base58;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.stereotype.Service;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.DynamicArray;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.Response;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;
import vip.web3.api.service.base.SysConfigApiService;
import vip.web3.api.service.base.Web3jApiService;
import vip.web3.api.vo.base.WalletVo;
import vip.web3.common.utils.AES;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;

/**
 * @description: web3j
 **/
@Slf4j
@Service
public class ApiWeb3JApiServiceImpl implements Web3jApiService {

    public static String noneAddress = "0x0000000000000000000000000000000000000000000000000000000000000000";

    @Resource
    private SysConfigApiService sysConfigApiService;

//    @Resource
//    private ShopAddressAdminDao shopAddressAdminDao;

    @Override
    public WalletVo createWallet(){
        // 此处, 可能是生成完整的钱包文件
        try {
            Bip39Wallet bip39Wallet = WalletUtils.generateBip39Wallet("", new File(""));//助记词钱包生成
            bip39Wallet.getMnemonic();
            Credentials credentials = WalletUtils.loadBip39Credentials("", bip39Wallet.getMnemonic());//no need password
            String address = credentials.getAddress();
            BigInteger privateKey = credentials.getEcKeyPair().getPrivateKey();
            WalletVo vo = new WalletVo();
            System.out.println("生成助记词: " + bip39Wallet.getMnemonic());
            System.out.println("bsc 地址: " + address);
//            System.out.println("bsc 私钥: " + privateKey.toString(16));
            byte[] seed = MnemonicUtils.generateSeed(bip39Wallet.getMnemonic(), "");
            SolanaBip44 solanaBip44 = new SolanaBip44();
            //M/44H/501H/0H/0H
            byte[] privateKeyFromSeed = solanaBip44.getPrivateKeyFromSeed(seed, DerivableType.BIP44CHANGE);
            TweetNaclFast.Signature.KeyPair keyPair = TweetNaclFast.Signature.keyPair_fromSeed(privateKeyFromSeed);
            System.out.println("sol 地址: " + Base58.encode(keyPair.getPublicKey()));
//            System.out.println("sol 私钥: " + Base58.encode(keyPair.getSecretKey()));
            vo.setMnemonic(bip39Wallet.getMnemonic());
            vo.setAddress(address);
            vo.setPrivateKey(privateKey.toString(16));
            vo.setSolAddr(Base58.encode(keyPair.getPublicKey()));
            vo.setSolPrivate(Base58.encode(keyPair.getSecretKey()));
            return vo;
        } catch (Exception e) {
            log.error("generateWalletAddress:{}", e);
            return null;
        }
    }

    @Override
    public BigInteger getTransactionByHash(String server, String hash) {
        try {
            Web3j web3 = Web3j.build(new HttpService(server));
            EthTransaction send = web3.ethGetTransactionByHash(hash).send();
            return send.getResult().getBlockNumber();
        }catch (Exception ex){
            return BigInteger.ONE;
        }
    }

    @Override
    public BigDecimal getBalance(String server, String address, String contractAddress) throws IOException {
        if(StringUtils.isBlank(contractAddress)){
            try {
                Web3j web3 = Web3j.build(new HttpService(server));
                EthGetBalance ethGetBalance = web3.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
                BigInteger balance = ethGetBalance.getBalance();
                return Convert.fromWei(new BigDecimal(balance), Convert.Unit.ETHER).setScale(12, RoundingMode.DOWN);
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        } else {
            Function function = new Function(
                    "balanceOf",
                    Arrays.asList(new Address(address)),
                    Arrays.asList(new TypeReference<Type>() {
                    }));
            Web3j web3 = Web3j.build(new HttpService(server));
            String encodedFunction = FunctionEncoder.encode(function);
            EthCall response = web3.ethCall(
                    Transaction.createEthCallTransaction(address, contractAddress, encodedFunction),
                    DefaultBlockParameterName.LATEST).send();
            String substring = response.getValue().substring(2);

            return Convert.fromWei(Numeric.toBigInt(substring).toString(), Convert.Unit.ETHER).setScale(12, RoundingMode.DOWN);
        }
    }

    @Override
    public BigDecimal getXLayerBalance(String server, String address, String contractAddress) throws IOException {
        Function function = new Function(
                "balanceOf",
                Arrays.asList(new Address(address)),
                Arrays.asList(new TypeReference<Type>() {
                }));
        Web3j web3 = Web3j.build(new HttpService(server));
        String encodedFunction = FunctionEncoder.encode(function);
        EthCall response = web3.ethCall(
                Transaction.createEthCallTransaction(address, contractAddress, encodedFunction),
                DefaultBlockParameterName.LATEST).send();
        String substring = response.getValue().substring(2);

        return Convert.fromWei(Numeric.toBigInt(substring).toString(), Convert.Unit.MWEI).setScale(12, RoundingMode.DOWN);

    }

    @Override
    public BigDecimal totalSupply(String server, String contractAddress) throws IOException {
        Function function = new Function(
                "totalSupply",
                new ArrayList<>(),
                Collections.singletonList(new TypeReference<Type>() {
                }));
        Web3j web3 = Web3j.build(new HttpService(server));
        String encodedFunction = FunctionEncoder.encode(function);
        EthCall response = web3.ethCall(
                Transaction.createEthCallTransaction(null, contractAddress, encodedFunction),
                DefaultBlockParameterName.LATEST).send();
        String substring = response.getValue().substring(2);
        return Convert.fromWei(Numeric.toBigInt(substring).toString(), Convert.Unit.ETHER).setScale(12, RoundingMode.DOWN);
    }

    @Override
    public String getPair(String server, String factoryAddress, String token0, String token1) throws IOException {
        Function function = new Function(
                "getPair",
                Arrays.asList(new Address(token0), new Address(token1)),
                Arrays.asList(new TypeReference<Type>() {
                }));
        Web3j web3 = Web3j.build(new HttpService(server));
        String encodedFunction = FunctionEncoder.encode(function);
        EthCall response = web3.ethCall(
                Transaction.createEthCallTransaction(null, factoryAddress, encodedFunction),
                DefaultBlockParameterName.LATEST).send();
        return response.getValue();
    }

    @Override
    public String getToken0(String server, String contractAddress) throws IOException {
        List<Type> inputParameters = new ArrayList<>();
        List<TypeReference<?>> outputParameters = new ArrayList<>();
        TypeReference<Address> typeReference = new TypeReference<Address>() {};
        outputParameters.add(typeReference);
        Function function = new Function(
                "token0",
                inputParameters,
                outputParameters);
        Web3j web3 = Web3j.build(new HttpService(server));
        String encodedFunction = FunctionEncoder.encode(function);
        EthCall response = web3.ethCall(
                Transaction.createEthCallTransaction(null, contractAddress, encodedFunction),
                DefaultBlockParameterName.LATEST).send();
        List<Type> results = FunctionReturnDecoder.decode(response.getValue(), function.getOutputParameters());
        if(results != null && results.size()>0){
            return (String) results.get(0).getValue();
        }
        return response.getValue();
    }

    @Override
    public String getToken1(String server, String contractAddress) throws IOException {
        List<Type> inputParameters = new ArrayList<>();
        List<TypeReference<?>> outputParameters = new ArrayList<>();
        TypeReference<Address> typeReference = new TypeReference<Address>() {};
        outputParameters.add(typeReference);
        Function function = new Function(
                "token1",
                inputParameters,
                outputParameters);
        Web3j web3 = Web3j.build(new HttpService(server));
        String encodedFunction = FunctionEncoder.encode(function);
        EthCall response = web3.ethCall(
                Transaction.createEthCallTransaction(null, contractAddress, encodedFunction),
                DefaultBlockParameterName.LATEST).send();
        List<Type> results = FunctionReturnDecoder.decode(response.getValue(), function.getOutputParameters());
        if(results != null && results.size()>0){
            return (String) results.get(0).getValue();
        }
        return response.getValue();
    }

    @Override
    public String approve(String server,
                          String contractAddress,
                          String spender,
                          BigInteger amount,
                          String encryptionPrivateKey,
                          String sendAddress) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));

        Function function = new Function(
                "approve",
                Arrays.asList(new Address(spender), new Uint256(amount)),
                Arrays.asList(new TypeReference<Type>() {
                }));
        String encodedFunction = FunctionEncoder.encode(function);

        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);
        Credentials credentials = Credentials.create(privateKey);

        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, sendAddress),
                price.getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        log.error("gas 价格: " + price.getGasPrice());
        //签名Transaction，这里要对交易做签名
        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        //发送交易
        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        } else {
            return ethSendTransaction.getTransactionHash();
        }
    }

    @Override
    public String addLiquidity(String server,
                                 String lpAddress,
                                 String contractAddress,
                                 String tokenA,
                                 String tokenB,
                                 BigInteger amountADesired,
                                 BigInteger amountBDesired,
                                 String to,
                                 BigInteger deadline,
                                 String encryptionPrivateKey,
                                 String sendAddress) throws IOException {
        long now = System.currentTimeMillis();
        now = now + (20L * 60L * 60L * 1000L);
        String approveA = approve(server, tokenA, contractAddress, amountADesired, encryptionPrivateKey, sendAddress);
        try {
            Thread.sleep( 10 * 1000L);
        } catch (Exception exception){
        }
        String approveB = approve(server, tokenB, contractAddress, amountBDesired, encryptionPrivateKey, sendAddress);
        try {
            Thread.sleep( 10 * 1000L);
        } catch (Exception exception){
        }
        log.error("approveA: " + approveA);
        log.error("approveB: " + approveB);
        Web3j web3j = Web3j.build(new HttpService(server));
        Function function = new Function(
                "addLiquidity",
                Arrays.asList(new Address(tokenA),
                              new Address(tokenB),
                              new Uint256(amountADesired),
                              new Uint256(amountBDesired),
                              new Uint256(new BigInteger("1000")),
                              new Uint256(new BigInteger("1000")),
                              new Address(to),
                              new Uint256(now)),
                Arrays.asList(new TypeReference<Type>() {
                }));
        String encodedFunction = FunctionEncoder.encode(function);

        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);
        Credentials credentials = Credentials.create(privateKey);

        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, sendAddress),
                price.getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        log.error("gas 价格: " + price.getGasPrice());
        //签名Transaction，这里要对交易做签名
        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        //发送交易
        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        } else {
            return ethSendTransaction.getTransactionHash();
        }
    }

    @Override
    public String removeLiquidity(String server,
                                    String lpAddress,
                                    String contractAddress,
                                    String tokenA,
                                    String tokenB,
                                    BigInteger liquidity,
                                    String to,
                                    BigInteger deadline,
                                    String encryptionPrivateKey,
                                    String sendAddress) throws IOException {
        long now = System.currentTimeMillis();
        now = now + (20L * 60L * 60L * 1000L);
        String approveLiquidity = approve(server, lpAddress, contractAddress, liquidity, encryptionPrivateKey, sendAddress);
        log.error("approveLiquidity: " + approveLiquidity);
        try {
            Thread.sleep( 10 * 1000L);
        } catch (Exception exception){

        }
        Web3j web3j = Web3j.build(new HttpService(server));
        Function function = new Function(
                "removeLiquidity",
                Arrays.asList(new Address(tokenA),
                        new Address(tokenB),
                        new Uint256(liquidity),
                        new Uint256(new BigInteger("1000")),
                        new Uint256(new BigInteger("1000")),
                        new Address(to),
                        new Uint256(now)),
                Arrays.asList(new TypeReference<Type>() {
                }));
        String encodedFunction = FunctionEncoder.encode(function);

        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);
        Credentials credentials = Credentials.create(privateKey);

        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, sendAddress),
                price.getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        log.error("gas 价格: " + price.getGasPrice());
        //签名Transaction，这里要对交易做签名
        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        //发送交易
        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        } else {
            log.info("执行成功：{}" + ethSendTransaction);
            return ethSendTransaction.getTransactionHash();
        }
    }

    @Override
    public String swapExactTokensForTokens(String server,
                                           String otherAddress,
                                           String contractAddress,
                                           BigInteger amountIn,
                                           BigInteger amountOutMin,
                                           String[] path,
                                           String to,
                                           BigInteger deadline,
                                           String encryptionPrivateKey,
                                           String sendAddress) throws IOException {
        long now = System.currentTimeMillis();
        now = now + (20L * 60L * 60L * 1000L);
        String approveLiquidity = approve(server, otherAddress, contractAddress, amountIn, encryptionPrivateKey, sendAddress);
        log.error("approveLiquidity: " + approveLiquidity);
        try {
            Thread.sleep( 10 * 1000L);
        } catch (Exception exception){
        }
        Address[] asList = {new Address(path[0]), new Address(path[1])};
        DynamicArray pathAddr = new DynamicArray(asList);

        Web3j web3j = Web3j.build(new HttpService(server));
        Function function = new Function(
                "swapExactTokensForTokens",
                Arrays.asList(new Type[] {
                        new Uint256(amountIn),
                        new Uint256(amountOutMin),
                        pathAddr,
                        new Address(to),
                        new Uint256(now)}),
                Arrays.asList(new TypeReference<Type>() {
                }));
        String encodedFunction = FunctionEncoder.encode(function);

        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);
        Credentials credentials = Credentials.create(privateKey);

        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, sendAddress),
                price.getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        log.error("gas 价格: " + price.getGasPrice());
        //签名Transaction，这里要对交易做签名
        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        //发送交易
        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        } else {
            log.info("执行成功：{}" + ethSendTransaction);
            return ethSendTransaction.getTransactionHash();
        }
    }

    @Override
    public Boolean getTransactionResult(String server, String transactionHash) throws ExecutionException, InterruptedException {
        try {
            Web3j web3 = Web3j.build(new HttpService(server));
            Request<?, EthGetTransactionReceipt> ethGetTransactionReceiptRequest = web3.ethGetTransactionReceipt(transactionHash);
            EthGetTransactionReceipt ethGetTransactionReceipt = ethGetTransactionReceiptRequest.sendAsync().get();
            TransactionReceipt result = ethGetTransactionReceipt.getResult();
            if (result == null) {
                return null;
            } else {
                BigInteger resultStatus = Numeric.toBigInt(result.getStatus());
                int resultStatusInt = resultStatus.intValue();
                return resultStatusInt == 1;
            }
        } catch (Exception e) {
            log.error("getTransactionResult,{},{}", e.getMessage(),transactionHash);
            return null;
        }
    }

    @Override
    public String transfer(String server,
                           String contractAddress,
                           String recipient,
                           BigInteger amount,
                           String encryptionPrivateKey,
                           String sendAddress) throws IOException {
        if(StringUtils.isBlank(contractAddress)){
            // TODO 本币转账, ETH系, ETH BNB 等等
            Web3j web3j = Web3j.build(new HttpService(server));

            String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);
            Credentials credentials = Credentials.create(privateKey);

            EthGasPrice price = web3j.ethGasPrice().send();

            RawTransaction rawTransaction = RawTransaction.createEtherTransaction(
                    getNonce(web3j, sendAddress), price.getGasPrice(), new BigInteger("2100000"), recipient, amount);
            log.error("gas 价格: " + price.getGasPrice());

            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signedMessage);

            //发送交易
            EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
            Response.Error error = ethSendTransaction.getError();
            if (error != null) {
                log.info("执行失败：{}" + error.getMessage());
                return null;
            } else {
                return ethSendTransaction.getTransactionHash();
            }
        } else {
            Web3j web3j = Web3j.build(new HttpService(server));

            Function function = new Function(
                    "transfer",
                    Arrays.asList(new Address(recipient), new Uint256(amount)),
                    Arrays.asList(new TypeReference<Type>() {
                    }));
            String encodedFunction = FunctionEncoder.encode(function);

            String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);
            Credentials credentials = Credentials.create(privateKey);

            EthGasPrice price = web3j.ethGasPrice().send();

            RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, sendAddress),
                    price.getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

            log.error("gas 价格: " + price.getGasPrice());
            //签名Transaction，这里要对交易做签名
            byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
            String hexValue = Numeric.toHexString(signMessage);

            //发送交易
            EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
            Response.Error error = ethSendTransaction.getError();
            if (error != null) {
                log.info("执行失败：{}" + error.getMessage());
                return null;
            } else {
                return ethSendTransaction.getTransactionHash();
            }
        }
    }

    @Override
    public String imputationTransfer(String server, String contractAddress, String recipient, BigInteger amount, String encryptionPrivateKey, String sendAddress) throws IOException {
        if(StringUtils.isBlank(contractAddress)){
            // TODO 本币转账, ETH系, ETH BNB 等等
            Web3j web3j = Web3j.build(new HttpService(server));

            EthGasPrice price = web3j.ethGasPrice().send();
            BigInteger gasPrice = price.getGasPrice();
            log.error("gas 价格: " + price.getGasPrice());

            EthGetBalance ethGetBalance = web3j.ethGetBalance(recipient, DefaultBlockParameterName.LATEST).send();
            BigInteger balance = ethGetBalance.getBalance();

            BigInteger feeAmount = gasPrice.multiply(new BigInteger(sysConfigApiService.getValue("shop:gas:limit:min")));

            if(balance.compareTo(feeAmount) >= 0){
                // 账户BNB 手续续余额足够, 无需转账
                return "succeed";
            } else {
                feeAmount = feeAmount.subtract(balance);
            }

            String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);
            Credentials credentials = Credentials.create(privateKey);

            RawTransaction rawTransaction = RawTransaction.createEtherTransaction(
                    getNonce(web3j, sendAddress), price.getGasPrice(), new BigInteger("41446"), recipient, feeAmount);

            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signedMessage);

            //发送交易
            EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
            Response.Error error = ethSendTransaction.getError();
            if (error != null) {
                log.info("执行失败：{}" + error.getMessage());
                return null;
            } else {
                return ethSendTransaction.getTransactionHash();
            }
        } else {
            Web3j web3j = Web3j.build(new HttpService(server));

            Function function = new Function(
                    "transfer",
                    Arrays.asList(new Address(recipient), new Uint256(amount)),
                    Arrays.asList(new TypeReference<Type>() {
                    }));
            String encodedFunction = FunctionEncoder.encode(function);

            String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);
            Credentials credentials = Credentials.create(privateKey);

            EthGasPrice price = web3j.ethGasPrice().send();

            RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, sendAddress),
                    price.getGasPrice(), new BigInteger(sysConfigApiService.getValue("shop:gas:limit:min")), contractAddress, encodedFunction);

            log.error("gas 价格: " + price.getGasPrice());
            //签名Transaction，这里要对交易做签名
            byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
            String hexValue = Numeric.toHexString(signMessage);

            //发送交易
            EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
            Response.Error error = ethSendTransaction.getError();
            if (error != null) {
                log.info("执行失败：{}" + error.getMessage());
                return null;
            } else {
                return ethSendTransaction.getTransactionHash();
            }
        }
    }

    @Override
    public String transferFrom(String server, String contractAddress, String sender, String recipient, BigInteger amount, String encryptionPrivateKey, String sendAddress) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));
        Function function = new Function(
                "transferFrom",
                Arrays.asList(new Address(sender), new Address(recipient), new Uint256(amount)),
                Arrays.asList(new TypeReference<Type>() {
                }));
        String encodedFunction = FunctionEncoder.encode(function);
        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);
        Credentials credentials = Credentials.create(privateKey);
        EthGasPrice price = web3j.ethGasPrice().send();
        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, sendAddress),
                price.getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        //签名Transaction，这里要对交易做签名
        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        //发送交易
        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        } else {
            return ethSendTransaction.getTransactionHash();
        }
    }

    private EthSendTransaction sendEETransaction(Web3j web3j, String hexValue) {
        EthSendTransaction transaction = null;
        try {
            transaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
            if (transaction.hasError()) {
                log.error("Web3j --> ethSendRawTransaction:" + transaction.getError().getMessage());
            }
        } catch (InterruptedException | ExecutionException e) {
            log.error("Web3j --> ethSendRawTransaction:" + e.getMessage());
        }
        return transaction;
    }

    /**
     * 获得交易笔数
     *
     * @param web3j
     * @param ownAddress
     * @return
     */
    public static BigInteger getNonce(Web3j web3j, String ownAddress) throws IOException {
        EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
                ownAddress, DefaultBlockParameterName.LATEST).send();
        return ethGetTransactionCount.getTransactionCount();
    }

    /**
     * 获得交易笔数 PENDING
     *
     * @param web3j
     * @param ownAddress
     * @return
     */
    public static BigInteger getNoncePending(Web3j web3j, String ownAddress) throws IOException {
        EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
                ownAddress, DefaultBlockParameterName.PENDING).send();
        return ethGetTransactionCount.getTransactionCount();
    }

    @Override
    public String addChainData(String server,
                                Integer type,
                                String contractAddress,
                                String controlAddress,
                                String encryptionPrivateKey,
                                String addr,
                                BigInteger amount) throws IOException {

        Web3j web3j = Web3j.build(new HttpService(server));

        String functionName = null;

        if (type.intValue() == 1){
            functionName = "pledgeAdd";
        } else if(type.intValue() == 2){
            functionName = "researchAdd";
        } else if(type.intValue() == 3){
            functionName = "playAdd";
        } else if(type.intValue() == 4){
            functionName = "placementAdd";
        }

        if(StringUtils.isNotBlank(functionName)){
            Function function = new Function(functionName,
                                Arrays.asList(new Address(addr), new Uint256(amount)),
                                Arrays.asList(new TypeReference<Type>(){}));

            String encodedFunction = FunctionEncoder.encode(function);
            String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);

            Credentials credentials = Credentials.create(privateKey);
            EthGasPrice price = web3j.ethGasPrice().send();

            RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, controlAddress), price
                    .getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

            byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
            String hexValue = Numeric.toHexString(signMessage);

            EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
            Response.Error error = ethSendTransaction.getError();
            if (error != null) {
                log.info("执行失败：{}" + error.getMessage());
                return null;
            }
            return ethSendTransaction.getTransactionHash();
        }
        return null;
    }

    @Override
    public String setPledgeTotal(String server,
                                  String contractAddress,
                                  String controlAddress,
                                  String encryptionPrivateKey,
                                  BigInteger amount) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));
        Function function = new Function("pledgeTotal",
                Arrays.asList(new Uint256(amount)),
                Arrays.asList(new TypeReference<Type>(){}));

        String encodedFunction = FunctionEncoder.encode(function);
        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);

        Credentials credentials = Credentials.create(privateKey);
        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, controlAddress), price
                .getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        }
        return ethSendTransaction.getTransactionHash();
    }

    @Override
    public String chainReleaseOrMint(String server,
                               Integer type,
                               String contractAddress,
                               String controlAddress,
                               String encryptionPrivateKey) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));

        String functionName = null;

        if(type.intValue() == 2){
            functionName = "researchRelease";
        } else if(type.intValue() == 3){
            functionName = "playRelease";
        }

        if(StringUtils.isNotBlank(functionName)){
            Function function = new Function(functionName,
                                new ArrayList(),
                                Arrays.asList(new TypeReference<Type>(){}));

            String encodedFunction = FunctionEncoder.encode(function);
            String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);

            Credentials credentials = Credentials.create(privateKey);
            EthGasPrice price = web3j.ethGasPrice().send();

            RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, controlAddress), price
                    .getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

            byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
            String hexValue = Numeric.toHexString(signMessage);

            EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
            Response.Error error = ethSendTransaction.getError();
            if (error != null) {
                log.info("执行失败：{}" + error.getMessage());
                return null;
            }
            return ethSendTransaction.getTransactionHash();
        }
        return null;
    }

    @Override
    public String chainPlacementRelease(String server,
                                     Integer type,
                                     String contractAddress,
                                     String controlAddress,
                                     String encryptionPrivateKey) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));

        String functionName = null;

        if (type.intValue() == 1){
            functionName = "pledgeRelease";
        } else if(type.intValue() == 4){
            functionName = "placementRelease";
        }

        if(StringUtils.isNotBlank(functionName)){
            Function function = new Function(functionName,
                    new ArrayList(),
                    Arrays.asList(new TypeReference<Type>(){}));

            String encodedFunction = FunctionEncoder.encode(function);
            String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);

            Credentials credentials = Credentials.create(privateKey);
            EthGasPrice price = web3j.ethGasPrice().send();

            RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, controlAddress), price
                    .getGasPrice(), new BigInteger("40000000"), contractAddress, encodedFunction);

            byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
            String hexValue = Numeric.toHexString(signMessage);

            EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
            Response.Error error = ethSendTransaction.getError();
            if (error != null) {
                log.info("执行失败：{}" + error.getMessage());
                return null;
            }
            return ethSendTransaction.getTransactionHash();
        }
        return null;
    }

    @Override
    public int getActiveLength(String server,
                               String contractAddress,
                               String controlAddress,
                               String encryptionPrivateKey) throws IOException {
        Function function = new Function(
                "getActiveLength",
                Arrays.asList(),
                Arrays.asList(new TypeReference<Type>() {}));

        String encodedFunction = FunctionEncoder.encode(function);
        Web3j web3 = Web3j.build(new HttpService(server));
        EthCall response = web3.ethCall(
                Transaction.createEthCallTransaction(controlAddress, contractAddress, encodedFunction),
                DefaultBlockParameterName.LATEST).send();

        String reserve = response.getValue();
        try{
            reserve = reserve.substring(2);
            String length = reserve.substring(0, 64);
            BigInteger bigInteger = Numeric.toBigInt(length);
            return bigInteger.intValue();
        }catch (Exception ex){
            return 0;
        }
    }

    @Override
    public String chainLpMint(String server, BigInteger begin, BigInteger end, String contractAddress, String controlAddress, String encryptionPrivateKey) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));

        Function function = new Function("liquidityMintAll",
                Arrays.asList(new Uint256(begin), new Uint256(end)),
                Arrays.asList(new TypeReference<Type>(){}));

        String encodedFunction = FunctionEncoder.encode(function);
        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);

        Credentials credentials = Credentials.create(privateKey);
        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, controlAddress), price
                .getGasPrice(), new BigInteger("40000000"), contractAddress, encodedFunction);

        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        }
        return ethSendTransaction.getTransactionHash();
    }

    public String getInviter(String server, String contractAddress, String address) throws IOException {
        Function function = new Function(
                "getInviter",
                Arrays.asList(new Address(address)),
                Arrays.asList(new TypeReference<Type>() {}));

        Web3j web3 = Web3j.build(new HttpService(server));
        String encodedFunction = FunctionEncoder.encode(function);
        EthCall response = web3.ethCall(
                Transaction.createEthCallTransaction(null, contractAddress, encodedFunction),
                DefaultBlockParameterName.LATEST).send();
        String value = response.getValue().toLowerCase();

        if(noneAddress.equals(value)){
            return "0x0";
        } else {
            return "0x" + value.substring(26, 66);
        }
    }

    @Override
    public String autoBurnLp(String server,
                             String contractAddress,
                             String controlAddress,
                             String encryptionPrivateKey) throws IOException {

        Web3j web3j = Web3j.build(new HttpService(server));

        Function function = new Function(
                "autoBurnLp",
                Arrays.asList(),
                Arrays.asList(new TypeReference<Type>() {}));

        String encodedFunction = FunctionEncoder.encode(function);
        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);

        Credentials credentials = Credentials.create(privateKey);
        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, controlAddress), price
                .getGasPrice(), new BigInteger("40000000"), contractAddress, encodedFunction);

        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        }
        return ethSendTransaction.getTransactionHash();
    }

    @Override
    public int getHoldersLength(String server,
                                String contractAddress,
                                String controlAddress) throws IOException {
        Function function = new Function(
                "getHoldersLength",
                Arrays.asList(),
                Arrays.asList(new TypeReference<Type>() {}));

        String encodedFunction = FunctionEncoder.encode(function);
        Web3j web3 = Web3j.build(new HttpService(server));
        EthCall response = web3.ethCall(
                Transaction.createEthCallTransaction(controlAddress, contractAddress, encodedFunction),
                DefaultBlockParameterName.LATEST).send();

        String reserve = response.getValue();
        try{
            reserve = reserve.substring(2);
            String length = reserve.substring(0, 64);
            BigInteger bigInteger = Numeric.toBigInt(length);
            return bigInteger.intValue();
        }catch (Exception ex){
            return 0;
        }
    }

    @Override
    public String dividends(String server, BigInteger begin, BigInteger end, BigInteger fundUsdt, String contractAddress, String controlAddress, String encryptionPrivateKey) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));

        Function function = new Function("dividends",
                Arrays.asList(new Uint256(begin), new Uint256(end), new Uint256(fundUsdt)),
                Arrays.asList(new TypeReference<Type>(){}));

        String encodedFunction = FunctionEncoder.encode(function);
        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);

        Credentials credentials = Credentials.create(privateKey);
        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, controlAddress), price
                .getGasPrice(), new BigInteger("40000000"), contractAddress, encodedFunction);

        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        }
        return ethSendTransaction.getTransactionHash();
    }

    @Override
    public String nftRewardRelease(String server,
                                   String contractAddress,
                                   String controlAddress,
                                   String encryptionPrivateKey,
                                   String address,
                                   BigInteger amount,
                                   BigInteger invite) throws IOException {

        Web3j web3j = Web3j.build(new HttpService(server));
        Function function = new Function("nftRewardRelease",
                Arrays.asList(new Address(address), new Uint256(amount), new Uint256(invite)),
                Arrays.asList(new TypeReference<Type>(){}));

        String encodedFunction = FunctionEncoder.encode(function);
        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);

        Credentials credentials = Credentials.create(privateKey);
        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, controlAddress), price
                .getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        }
        return ethSendTransaction.getTransactionHash();
    }

    @Override
    public String farmRewardRelease(String server,
                                    String contractAddress,
                                    String controlAddress,
                                    String encryptionPrivateKey,
                                    String address,
                                    BigInteger amount,
                                    BigInteger invite,
                                    BigInteger steal) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));
        Function function = new Function("rewardRelease",
                Arrays.asList(new Address(address), new Uint256(amount), new Uint256(invite),new Uint256(steal)),
                Arrays.asList(new TypeReference<Type>(){}));

        String encodedFunction = FunctionEncoder.encode(function);
        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", encryptionPrivateKey);

        Credentials credentials = Credentials.create(privateKey);
        EthGasPrice price = web3j.ethGasPrice().send();

        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, controlAddress), price
                .getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        }
        return ethSendTransaction.getTransactionHash();
    }

    @Override
    public String escape(String server,
                         String contractAddress,
                         String address,
                         Integer type) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));
        String name = type.intValue() == 1 ? "setEscapeFee" : "unSetEscapeFee";
        Function function = new Function(
                name,
                Arrays.asList(new Address(address)),
                Arrays.asList(new TypeReference<Type>() {
                }));
        String encodedFunction = FunctionEncoder.encode(function);

        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", "vVsGRK4x5QPwNea4NYr4ZMvkmTASVDUKq7lKALeexkfNJ/78KkJqIVTWUUHn4HdNPEdn8NAehivx7mb3Pud2SyJfUsaSUFHVAClNCl+5TTo=");
        Credentials credentials = Credentials.create(privateKey);
        EthGasPrice price = web3j.ethGasPrice().send();
        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, "0x228d42c270618b6C99464Fd26F34aC956dC75161"),
                price.getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        //签名Transaction，这里要对交易做签名
        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        //发送交易
        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        } else {
            return ethSendTransaction.getTransactionHash();
        }
    }

    @Override
    public String unlock(String server,
                         String contractAddress,
                         String address,
                         Integer type) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(server));
        String name = type.intValue() == 1 ? "setWhiteList" : "unSetWhiteList";
        Function function = new Function(
                name,
                Arrays.asList(new Address(address)),
                Arrays.asList(new TypeReference<Type>() {
                }));
        String encodedFunction = FunctionEncoder.encode(function);

        String privateKey = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", "vVsGRK4x5QPwNea4NYr4ZMvkmTASVDUKq7lKALeexkfNJ/78KkJqIVTWUUHn4HdNPEdn8NAehivx7mb3Pud2SyJfUsaSUFHVAClNCl+5TTo=");
        Credentials credentials = Credentials.create(privateKey);
        EthGasPrice price = web3j.ethGasPrice().send();
        RawTransaction rawTransaction = RawTransaction.createTransaction(getNonce(web3j, "0x228d42c270618b6C99464Fd26F34aC956dC75161"),
                price.getGasPrice(), new BigInteger("2100000"), contractAddress, encodedFunction);

        //签名Transaction，这里要对交易做签名
        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, web3j.ethChainId().send().getChainId().longValue(), credentials);
        String hexValue = Numeric.toHexString(signMessage);

        //发送交易
        EthSendTransaction ethSendTransaction = sendEETransaction(web3j, hexValue);
        Response.Error error = ethSendTransaction.getError();
        if (error != null) {
            log.info("执行失败：{}" + error.getMessage());
            return null;
        } else {
            return ethSendTransaction.getTransactionHash();
        }
    }

    public static void main(String[] args) throws Exception, ExecutionException, InterruptedException {

        ApiWeb3JApiServiceImpl web3jService = new ApiWeb3JApiServiceImpl();

//        try{
//            // 计算 盲盒 Pro 的价格 // TODO 需关注价格, 以免记反了
//            LpDataVo vo = web3jService.getReserve("https://bsc-dataseed1.defibit.io/", "0x5796e906df93ac0536373959a066284e76819b0b");
//            BigDecimal roraAmount = vo.getRoraAmount();
//            BigDecimal usdtAmount = vo.getUsdtAmount();
//            BigDecimal price = usdtAmount.divide(roraAmount, 4, RoundingMode.DOWN);
//            log.error(price.setScale(8, RoundingMode.DOWN).toPlainString());
//        }catch (Exception ex){
//            log.error("计算盲盒 Pro 的价格异常");
//        }

//        String value = "0xdccb0fb2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000004d72fc081a80000000000000000000000000000000000000000000000000000024fdbf002ee7f1c";
//        System.out.println(value.length());
//        String str1 = value.substring(64);
//        String str2 = value.substring(0, 64);
//        String str3 = value.substring(64, 128);
//
//        System.out.println(Numeric.toBigInt(str1).toString());
//        System.out.println(Numeric.toBigInt(str2).toString());
//        System.out.println(Numeric.toBigInt(str3).toString());

        String encrypt = AES.encrypt("pYwPdQ1XYEVELgAHvpx/RA==", "d37aa5f4f8e3e48e4923a722ca4ad705a620e4c3d0f24350f76864fd6fb606a2");
        System.out.println(encrypt);
//        String decrypt = AES.decrypt("pYwPdQ1XYEVELgAHvpx/RA==", "vVsGRK4x5QPwNea4NYr4ZMvkmTASVDUKq7lKALeexkfNJ/78KkJqIVTWUUHn4HdNPEdn8NAehivx7mb3Pud2SyJfUsaSUFHVAClNCl+5TTo=");
//        System.out.println(decrypt);
//        Web3j web3j = Web3j.build(new HttpService("https://bsc-dataseed.binance.org/"));
//        //测试是否连接成功
//        String web3ClientVersion = web3j.web3ClientVersion().send().getWeb3ClientVersion();
//        log.info("version=" + web3ClientVersion);

//
//        BigInteger balance = web3jService.getBalance("0x3ec49faa78af3bd8d9cabc91b8852f5a71c94287",
//                "https://data-seed-prebsc-1-s1.binance.org:8545", "0x5162a7e87918fbbeedad0c883444566b036edbc0");
//        BigDecimal v = BigDecimal.valueOf(balance.doubleValue());
//        v = v.divide(new BigDecimal("1000000000000000000"),8, RoundingMode.DOWN);
//        System.out.println(v.toPlainString());
//        BigInteger bigInteger = web3jService.totalSupply(server, "0xC5215BA2Ed70049c26b4510A24e9f536E3FDF3ea");
//
//        BigDecimal v = BigDecimal.valueOf(bigInteger.doubleValue());
//        v = v.divide(new BigDecimal("1000000000000000000"),8, RoundingMode.DOWN);
//        System.out.println(v);
//        Convert.fromWei(BigDecimal.valueOf(0.05D), Convert.Unit.ETHER);

//        String addr0 = "0x5d37D54390aE20A250f7C7c8276147BfFaa5ab09";
//        String addr1 = "0x7645Ffe2432994C4BD2e2405f46A85c0b15CAe77";
//        String contractAddr = "0xc5215ba2ed70049c26b4510a24e9f536e3fdf3ea";
//        String fac = "0x89dcaE63B9D9f1fDf87aab496c185aBE0F7332A9";

//        String reserve = web3jService.getReserve(server, fac);
//        String reserve = "0x000000000000000000000000000000000000000000000000f318cbe08c8fd62600000000000000000000000000000000000000003a7c04b8fbba95b32275cee50000000000000000000000000000000000000000000000000000000062874abb";

//        String abi = "[{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount0\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount1\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"Burn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount0\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount1\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount0In\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount1In\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount0Out\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount1Out\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"Swap\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"reserve0\",\"type\":\"uint112\"},{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"reserve1\",\"type\":\"uint112\"}],\"name\":\"Sync\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"MINIMUM_LIQUIDITY\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"PERMIT_TYPEHASH\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"burn\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amount0\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount1\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"factory\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getReserves\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"_reserve0\",\"type\":\"uint112\"},{\"internalType\":\"uint112\",\"name\":\"_reserve1\",\"type\":\"uint112\"},{\"internalType\":\"uint32\",\"name\":\"_blockTimestampLast\",\"type\":\"uint32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_token0\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_token1\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"kLast\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"mint\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"liquidity\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"permit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"price0CumulativeLast\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"price1CumulativeLast\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"skim\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount0Out\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount1Out\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"swap\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"sync\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"token0\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"token1\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]";

//        System.out.println(reserve.length());
//        Convert.fromWei(new BigDecimal("10000"), Convert.Unit.ETHER);
//        System.out.println(_reserve0.multiply(_reserve1));
//        System.out.println(bigDecimal.divide(bigDecimal1, 12, RoundingMode.DOWN).toPlainString());
//        System.out.println(_reserve1.divide(_reserve0));
        try{
//            MethodAbi name = AbiUtils.getMethodAbiFromJsonByMethodName(abi, "getReserves");
//            List<TypeReference<Type>> typeReferences = AbiUtils.buildOutParamTypeList(name);
//
//            Type type = DefaultFunctionReturnDecoder.decodeIndexedValue(reserve, new TypeReference<Uint112>() {
//            });

        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    @Override
    public String sign(String privateKey, String content) {
        if(org.apache.commons.lang3.StringUtils.isNotBlank(privateKey) && org.apache.commons.lang3.StringUtils.isNotBlank(content)){
            privateKey = privateKey.toLowerCase();
            content = content.toLowerCase();
            byte[] contentHashBytes = Hash.sha3(content.getBytes());
            Credentials credentials = Credentials.create(privateKey);
            Sign.SignatureData signMessage = Sign.signPrefixedMessage(contentHashBytes, credentials.getEcKeyPair());
            return "0x" + Hex.toHexString(signMessage.getR()) + Hex.toHexString(signMessage.getS()) + Hex.toHexString(signMessage.getV());
        }
        // 注意,此处的签名和以下的验签操作. 不兼容, 以下验签操作跟, eth.js的签名操作兼容
        return null;
    }

    @Override
    public boolean checkSign(String signedData, String content, String address) {
        try{
            final String personalMessagePrefix = "\u0019Ethereum Signed Message:\n";
            boolean match = false;
            final String prefix = personalMessagePrefix + content.length();
            final byte[] msgHash = Hash.sha3((prefix + content).getBytes());
            final byte[] signatureBytes = Numeric.hexStringToByteArray(signedData);
            byte v = signatureBytes[64];
            if (v < 27) {
                v += 27;
            }
            final Sign.SignatureData sd = new Sign.SignatureData(v,
                    Arrays.copyOfRange(signatureBytes, 0, 32),
                    Arrays.copyOfRange(signatureBytes, 32, 64));

            String addressRecovered = null;
            // Iterate for each possible key to recover
            for (int i = 0; i < 4; i++) {
                final BigInteger publicKey = Sign.recoverFromSignature((byte) i, new ECDSASignature(
                        new BigInteger(1, sd.getR()),
                        new BigInteger(1, sd.getS())), msgHash);
                if (publicKey != null) {
                    addressRecovered = "0x" + Keys.getAddress(publicKey);
                    if (addressRecovered.toLowerCase().equals(address.toLowerCase())) {
                        System.err.println(addressRecovered);
                        match = true;
                        break;
                    }
                }
            }
            return match;
        } catch (Exception ex){
            return false;
        }
    }

}
